This notebook contains code to execute plotting and exploratory statistics for replicating Goldman et al. (2022) on a subset of 3 language families: Uralic, Romance, and Niger-Congo. We begin by investigating predictors of accuracy drop between SIGMORPHON (2020) and Goldman et al. (2022), then investigate the relationship between raw accuracy on the Goldman et al. test data and various predictors.

Setup

Load Libaries

library(ggplot2)
library(tidyverse)
library(car)
library(mgcv)
require(splines)

Read in & Process the Data

# Read in the data 
df<-read.csv("replication/replication_complete.csv")

# Make the family column pretty 
df$Family = str_to_title(str_replace(df$Family, "_", " "))

# We're going to be making a lot of log-log plots, so lets make our lives easier:
df$log_test_acc_drop = -1*log((-1*df$test_acc_drop)+1)
df$log_train_lemma_diff = -1*log(-1*df$train_lemma_diff_raw)

Define Helper Functions

Here, we define helper functions to run our correlational statistics, evaluate our fitted models, and compare models fitted to a single predictor to models fitted to multiple predictors in order to evaluate which better explains the data.

# We'll use this helper function to run our stats
correlations <- function(a, b){
  for (m in list("pearson", "spearman", "kendall")){
    # Supressing warnings bc we'll get them whenever there are ties 
    suppressWarnings(res <- cor.test(a, b, method = m))
    formatted <- sprintf("%s: %f (p = %e)", res$method, res$estimate, res$p.value)
    print(formatted)
  }
}

# We'll use this helper function to get information about our model
eval_model <- function(model, df){
  rsquared = summary(model)$r.squared
  AIC = AIC(model)
  results <- sprintf("R^2: %f, AIC: %f", rsquared, AIC)
  print(results)
  layout(matrix(c(1,2,3,4),2,2)) 
  plot(model)
  return(predict(model, df, se = TRUE))
}

# We'll use this helper function to compare two models 
compare_models <- function(model_both, model_single){
  layout(matrix(c(1,2,3,4),2,2)) 
  plot(model_both)
  AIC = AIC(model_both)
  anovap = anova(model_both, model_single)$`Pr(>F)`[-1]
  results <- sprintf("AIC: %f, ANOVA p: %f", AIC, anovap)
  print(results)
  vif(model_both)
}

Investigating Accuracy Drop

We begin by investigating relationships between test accuracy drop (between SIGMORPHON 2020 & Goldman et al 2022) and various predictors. Exploratory analysis tells us that the following predictors have strong relationships with accuracy drop:

We begin by investigating unscaled scatter plots before fitting linear models and running statistics on the log-log scaled plots, and finally fitting non-linear models to these predictors.

Unscaled Scatter Plots

Training Size

df %>% 
  ggplot(aes(Goldman_train_size, test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training size") + 
  ylab("Test accuracy drop") + 
  ggtitle("Replication of Goldman et al.") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
        )

Training Lemmas

df %>% 
  ggplot(aes(Goldman_train_lemmas, test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training lemmas") + 
  ylab("Test accuracy drop") + 
  ggtitle("Test accuracy drop vs. training lemmas") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )

Training Lemma Drop

df %>% 
  ggplot(aes(train_lemma_diff_raw, test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Training lemma difference between SIGMORPHON & Goldman et al.") + 
  ylab("Test accuracy drop") + 
  ggtitle("Test accuracy drop vs. training lemma drop") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )

Linear Models and Correlations

From the above, we can see that the relationship between the various predictors and test accuracy drop isn’t linear. However, when both axes are log-scaled, the relationship is near linear. As such, we fit linear models and run correlation statistics on the log-log scaled version.

Training size vs. test accuracy drop

train_size_lm = lm(log_test_acc_drop ~ log(Goldman_train_size), data = df)
pred <- eval_model(train_size_lm, df)
[1] "R^2: 0.679104, AIC: 80.198992"

df %>% 
  ggplot(aes(log(Goldman_train_size), log_test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training size, log scale") + 
  ylab("Test accuracy drop, log scale") + 
  ggtitle("Test accuracy drop vs. training size") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  +
  geom_ribbon(aes(x = log(Goldman_train_size), 
                  ymin = pred$fit - 2 * pred$se.fit, 
                  ymax = pred$fit + 2 * pred$se.fit), 
              fill = "grey", 
              alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_size), y = pred$fit), color = "black")
ggsave("../writeup/figs/lm_drop_size.png", dpi=500)
Saving 6.94 x 4.29 in image

correlations(log(df$Goldman_train_size), df$log_test_acc_drop)
[1] "Pearson's product-moment correlation: 0.824078 (p = 2.118272e-09)"
[1] "Spearman's rank correlation rho: 0.798319 (p = 4.503624e-07)"
[1] "Kendall's rank correlation tau: 0.593583 (p = 1.359831e-07)"

Training lemmas vs. test accuracy drop

train_lemma_lm = lm(log_test_acc_drop ~ log(Goldman_train_lemmas), data = df)
pred <- eval_model(train_lemma_lm, df)
[1] "R^2: 0.818971, AIC: 60.735439"

df %>% 
  ggplot(aes(log(Goldman_train_lemmas), log_test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. number of training lemmas, log scale") + 
  ylab("Test accuracy drop, log scale") + 
  ggtitle("Test accuracy drop vs. number of training lemmas") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log(Goldman_train_lemmas), 
                 ymin = pred$fit - 2 * pred$se.fit, 
                 ymax = pred$fit + 2 * pred$se.fit), 
             fill = "grey", 
             alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_lemmas), y = pred$fit), color = "black")
ggsave("../writeup/figs/lm_drop_lemmas.png", dpi=500)
Saving 6.94 x 4.29 in image

correlations(log(df$Goldman_train_lemmas), df$log_test_acc_drop)
[1] "Pearson's product-moment correlation: 0.904970 (p = 2.044293e-13)"
[1] "Spearman's rank correlation rho: 0.915412 (p = 3.427937e-14)"
[1] "Kendall's rank correlation tau: 0.747098 (p = 5.694553e-10)"

Training lemma drop vs. test accuracy drop

lemma_drop_lm = lm(log_test_acc_drop ~ log_train_lemma_diff, data = df)
pred <- eval_model(lemma_drop_lm, df)
[1] "R^2: 0.799573, AIC: 64.196379"

df %>% 
  ggplot(aes(log_train_lemma_diff, log_test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Training lemma drop, log scale") + 
  ylab("Test accuracy drop, log scale") + 
  ggtitle("Test accuracy drop vs. training lemma drop") +
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log_train_lemma_diff, 
                 ymin = pred$fit - 2 * pred$se.fit, 
                 ymax = pred$fit + 2 * pred$se.fit), 
             fill = "grey", 
             alpha = .4) + 
  geom_line(aes(x = log_train_lemma_diff, y = pred$fit), color = "black")

ggsave("../writeup/figs/lm_drop_drop.png", dpi=500)
Saving 6.94 x 4.29 in image

correlations(df$log_train_lemma_diff, df$log_test_acc_drop)
[1] "Pearson's product-moment correlation: -0.894188 (p = 1.053677e-12)"
[1] "Spearman's rank correlation rho: -0.888906 (p = 2.209056e-12)"
[1] "Kendall's rank correlation tau: -0.726787 (p = 1.590860e-09)"

Co-Linearity

Intuitively, it makes sense that several of the possible predictors above would be co-linear: larger training data will generally contain more lemmas, for example. We investigate these co-linearities below.

Training size vs. training lemmas

df %>% 
  ggplot(aes(log(Goldman_train_size), log(Goldman_train_lemmas))) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training size, log scale") + 
  ylab("Goldman et al. number of training lemmas, log scale") + 
  ggtitle("Training size vs. training lemmas, log scale") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  stat_smooth(method="lm", color="black", size=0.5, alpha = 0.5)


correlations(log(df$Goldman_train_size), log(df$Goldman_train_lemmas))
[1] "Pearson's product-moment correlation: 0.892150 (p = 1.408484e-12)"
[1] "Spearman's rank correlation rho: 0.890655 (p = 1.736118e-12)"
[1] "Kendall's rank correlation tau: 0.722076 (p = 2.084901e-09)"

Training size vs. difference in training lemmas

df %>%
  ggplot(aes(log(Goldman_train_size), log_train_lemma_diff)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training size, log scale") + 
  ylab("Training lemma difference, log scale") + 
  ggtitle("Training size vs. training lemma drop") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  stat_smooth(method="lm", color="black", size=0.5, alpha = 0.5)


correlations(log(df$Goldman_train_size), df$log_train_lemma_diff)
[1] "Pearson's product-moment correlation: -0.921682 (p = 1.046724e-14)"
[1] "Spearman's rank correlation rho: -0.911828 (p = 6.486413e-14)"
[1] "Kendall's rank correlation tau: -0.762501 (p = 2.429011e-10)"

Training lemmas vs. difference in training lemmas

df %>%
  ggplot(aes(log(Goldman_train_lemmas), log_train_lemma_diff)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training lemmas, log scale") + 
  ylab("Training lemma difference, log scale") + 
  ggtitle("Training lemmas vs. training lemma drop") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  stat_smooth(method="lm", color="black", size=0.5, alpha = 0.5)


correlations(log(df$Goldman_train_lemmas), df$log_train_lemma_diff)
[1] "Pearson's product-moment correlation: -0.985909 (p = 2.005603e-26)"
[1] "Spearman's rank correlation rho: -0.978066 (p = 2.252220e-23)"
[1] "Kendall's rank correlation tau: -0.945390 (p = 4.816969e-15)"

Accuracy Drop Pirate Stats, Linear Version 🏴‍☠️

We now have three possible predictor variables which we know are co-linear with one another, so we want to disentangle these relationships to determine which variable(s) best predict drops in model performance.

Above, we saw that of the single-predictor linear models, test accuracy drop was best predicted by training lemmas. Using this as a starting point, we add additional predictors to the linear models to determine if any additional predictor leads to significantly better model performance, defined as follows:

  • Does the AIC drop by at least 2 units (significantly better)? 

  • Is the ANOVA p-value between the two models significant? (p < 0.05)

  • Is the VIF (a measure of co-linearity) low – below 3-4?

Lemmas + Train Size

We compare a model conditioned on the number of lemmas in train and the number of triples in train to one just conditioned on the number of lemmas.

lemmas_plus_train_lm = lm(log_test_acc_drop ~ log(Goldman_train_size) + log(Goldman_train_lemmas), data = df)
summary(lemmas_plus_train_lm)$r.squared
[1] 0.8203387
compare_models(lemmas_plus_train_lm, train_lemma_lm)
[1] "AIC: 62.477507, ANOVA p: 0.630477"
  log(Goldman_train_size) log(Goldman_train_lemmas) 
                 4.900301                  4.900301 

Lemmas + Lemma Drop

We compare a model conditioned on the number of lemmas in train and the lemma drop between SIGMORPHON and Goldman et al in train to one just conditioned on the number of lemmas.

lemmas_drop_lm = lm(log_test_acc_drop ~ log(Goldman_train_lemmas) + log_train_lemma_diff, data = df)
summary(lemmas_drop_lm)$r.squared
[1] 0.8191093
compare_models(lemmas_drop_lm, train_lemma_lm)
[1] "AIC: 62.709373, ANOVA p: 0.878457"
log(Goldman_train_lemmas)      log_train_lemma_diff 
                 35.73512                  35.73512 

Accuracy Drop Pirate Stats, Non-Linear Version 🏴‍☠️

Though the relationship is near-linear when we log-scale both axes, we can see from our residual plots above that there is some non-linearity remaining. As such, we train more general models using the natural cubic splines with df = 3.

Training size

ns_train_size_lm = lm(log_test_acc_drop ~ ns(log(Goldman_train_size), df=3), data = df)
pred <- eval_model(ns_train_size_lm, df)
[1] "R^2: 0.801390, AIC: 67.886671"

df %>% 
  ggplot(aes(log(Goldman_train_size), log_test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training size, log scale") + 
  ylab("Test accuracy drop, log scale") + 
  ggtitle("Test accuracy drop vs. training size") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  +
  geom_ribbon(aes(x = log(Goldman_train_size), 
                  ymin = pred$fit - 2 * pred$se.fit, 
                  ymax = pred$fit + 2 * pred$se.fit), 
              fill = "grey", 
              alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_size), y = pred$fit), color = "black")

ggsave("../writeup/figs/ns_drop_size.png", dpi=500)
Saving 6.94 x 4.29 in image

Train lemmas

ns_train_lemma_lm = lm(log_test_acc_drop ~ ns(log(Goldman_train_lemmas), df = 3), data = df)
pred <- eval_model(ns_train_lemma_lm, df)
[1] "R^2: 0.863321, AIC: 55.180630"

df %>% 
  ggplot(aes(log(Goldman_train_lemmas), log_test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. number of training lemmas, log scale") + 
  ylab("Test accuracy drop, log scale") + 
  ggtitle("Test accuracy drop vs. number of training lemmas") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log(Goldman_train_lemmas), 
                 ymin = pred$fit - 2 * pred$se.fit, 
                 ymax = pred$fit + 2 * pred$se.fit), 
             fill = "grey", 
             alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_lemmas), y = pred$fit), color = "black")

ggsave("../writeup/figs/ns_drop_lemmas.png", dpi=500)
Saving 6.94 x 4.29 in image

Training Lemma Drop

ns_lemma_drop_lm = lm(log_test_acc_drop ~ ns(log_train_lemma_diff, df = 3), data = df)
pred <- eval_model(ns_lemma_drop_lm, df)
[1] "R^2: 0.841120, AIC: 60.298126"

df %>% 
  ggplot(aes(log_train_lemma_diff, log_test_acc_drop)) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Training lemma drop, log scale") + 
  ylab("Test accuracy drop, log scale") + 
  ggtitle("Test accuracy drop vs. training lemma drop") +
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log_train_lemma_diff, 
                 ymin = pred$fit - 2 * pred$se.fit, 
                 ymax = pred$fit + 2 * pred$se.fit), 
             fill = "grey", 
             alpha = .4) + 
  geom_line(aes(x = log_train_lemma_diff, y = pred$fit), color = "black")

ggsave("../writeup/figs/ns_drop_drop.png", dpi=500)
Saving 6.94 x 4.29 in image

Having trained the preliminary single-predictor models, we can once again train the more complex multi-predictor models and compare them in terms of AIC, ANOVA p-value, and VIF as above:

Lemmas + Train Size

ns_lemmas_plus_train_lm = lm(log_test_acc_drop ~ ns(log(Goldman_train_size), df = 3) + ns(log(Goldman_train_lemmas), df = 3), data = df)
summary(ns_lemmas_plus_train_lm)$r.squared
[1] 0.8868365
compare_models(ns_lemmas_plus_train_lm, ns_train_lemma_lm)
[1] "AIC: 54.761370, ANOVA p: 0.158460"
                                          GVIF Df GVIF^(1/(2*Df))
ns(log(Goldman_train_size), df = 3)   40.10359  3        1.850109
ns(log(Goldman_train_lemmas), df = 3) 40.10359  3        1.850109

Lemmas + Lemma Drop

ns_lemmas_drop_lm = lm(log_test_acc_drop ~ ns(log(Goldman_train_lemmas), df = 3) + ns(log_train_lemma_diff, df = 3), data = df)
summary(ns_lemmas_drop_lm)$r.squared
[1] 0.8697183
compare_models(ns_lemmas_drop_lm, ns_train_lemma_lm)
[1] "AIC: 59.550770, ANOVA p: 0.724908"
                                          GVIF Df GVIF^(1/(2*Df))
ns(log(Goldman_train_lemmas), df = 3) 219354.3  3        7.765886
ns(log_train_lemma_diff, df = 3)      219354.3  3        7.765886

Investigating Raw Accuracy

Having investigated the relationships with accuracy drop, we now wish to understand the predictors of raw accuracy. To begin, we visualize the relationship with raw accuracy for both SIGMORPHON and Goldman et al.

Basic Visualization

First, format the data for easier plotting:

Gold <- df %>% 
  select(train=Goldman_train_size, 
         lemmas = Goldman_train_lemmas,
         test = Goldman_test_acc, 
         Family = Family
         ) 
Gold$Type = "Goldman"

Sigm <- df %>% 
  select(train = SIGMORPHON_train_size,
         lemmas = SIGMORPHON_train_lemmas,
         test = SIGMORPHON_test_acc,
         Family = Family
         )
Sigm$Type = "SIGMORPHON"

new_df = rbind(Gold, Sigm)

Training Size

new_df %>% 
  ggplot(aes(log(train), log(test + 1), color = Type)) +
  geom_point(aes(shape = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  stat_smooth(aes(color=Type), method="lm", size=0.5, alpha = 0.5)+ 
  theme_bw() + 
  xlab("Training size, log scale") + 
  ylab("Test accuracy, log scale") + 
  ggtitle("Test accuracy vs. training size") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  ) 

print("-------GOLDMAN ------")
[1] "-------GOLDMAN ------"
correlations(log(df$Goldman_train_size), log(df$Goldman_test_acc + 1))
[1] "Pearson's product-moment correlation: 0.703670 (p = 3.443305e-06)"
[1] "Spearman's rank correlation rho: 0.817515 (p = 3.619313e-09)"
[1] "Kendall's rank correlation tau: 0.621986 (p = 2.456525e-07)"
print("-------SIGMORPHON -------")
[1] "-------SIGMORPHON -------"
correlations(log(df$SIGMORPHON_train_size), log(df$SIGMORPHON_test_acc + 1))
[1] "Pearson's product-moment correlation: 0.308926 (p = 7.544833e-02)"
[1] "Spearman's rank correlation rho: 0.039633 (p = 8.238934e-01)"
[1] "Kendall's rank correlation tau: 0.008993 (p = 9.408048e-01)"

Training Lemmas

new_df %>% 
  ggplot(aes(log(lemmas), log(test + 1), color = Type)) +
  geom_point(aes(shape = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  stat_smooth(aes(color=Type), method="lm", size=0.5, alpha = 0.5) + 
  theme_bw() + 
  xlab("Training lemmas, log scale") + 
  ylab("Test accuracy, log scale") + 
  ggtitle("Test accuracy vs. training lemmas") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  ) 

print("-------GOLDMAN ------")
[1] "-------GOLDMAN ------"
correlations(log(df$Goldman_train_lemmas), log(df$Goldman_test_acc + 1))
[1] "Pearson's product-moment correlation: 0.663836 (p = 1.868570e-05)"
[1] "Spearman's rank correlation rho: 0.849805 (p = 2.052872e-10)"
[1] "Kendall's rank correlation tau: 0.646953 (p = 8.530164e-08)"
print("-------SIGMORPHON -------")
[1] "-------SIGMORPHON -------"
correlations(log(df$SIGMORPHON_train_lemmas), log(df$SIGMORPHON_test_acc + 1))
[1] "Pearson's product-moment correlation: 0.155920 (p = 3.785560e-01)"
[1] "Spearman's rank correlation rho: -0.002142 (p = 9.904054e-01)"
[1] "Kendall's rank correlation tau: -0.057608 (p = 6.345697e-01)"

Co-Linearities

print("-------GOLDMAN ------")
[1] "-------GOLDMAN ------"
correlations(log(df$Goldman_train_size), log(df$Goldman_train_lemmas))
[1] "Pearson's product-moment correlation: 0.892150 (p = 1.408484e-12)"
[1] "Spearman's rank correlation rho: 0.890655 (p = 1.736118e-12)"
[1] "Kendall's rank correlation tau: 0.722076 (p = 2.084901e-09)"
print("-------SIGMORPHON -------")
[1] "-------SIGMORPHON -------"
correlations(log(df$SIGMORPHON_train_size), log(df$SIGMORPHON_train_lemmas))
[1] "Pearson's product-moment correlation: 0.904018 (p = 2.380841e-13)"
[1] "Spearman's rank correlation rho: 0.891283 (p = 1.590692e-12)"
[1] "Kendall's rank correlation tau: 0.717217 (p = 2.521127e-09)"

Raw Accuracy Pirate Stats, Linear Version 🏴‍☠️

As we did for accuracy drop, we now fit linear models to raw accuracy on the Goldman et al. data only (since there seems to be little effect for the SIGMORPHON data). We then compare the models fitted to just training size or training lemmas to the model fitted to both in the same way as above.

Training Size

train_size_lm = lm(log(Goldman_test_acc + 1) ~ log(Goldman_train_size), data = df)
pred <- eval_model(train_size_lm, df)
[1] "R^2: 0.495151, AIC: 101.312023"

df %>% 
  ggplot(aes(log(Goldman_train_size), log(Goldman_test_acc + 1))) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training size, log scale") + 
  ylab("Goldman et al. test accuracy, log scale") + 
  ggtitle("Goldman et al. test accuracy vs. training size") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log(Goldman_train_size), 
                  ymin = pred$fit - 2 * pred$se.fit, 
                  ymax = pred$fit + 2 * pred$se.fit), 
              fill = "grey", 
              alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_size), y = pred$fit), color = "black") 
ggsave("../writeup/figs/lm_raw_size.png", dpi=500)
Saving 6.94 x 4.29 in image

Number of Training Lemmas

train_lemma_lm = lm(log(Goldman_test_acc + 1) ~ log(Goldman_train_lemmas), data = df)
pred <- eval_model(train_lemma_lm)
[1] "R^2: 0.440678, AIC: 104.795841"

df %>% 
  ggplot(aes(log(Goldman_train_lemmas), log(Goldman_test_acc + 1))) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. number of training lemmas, log scale") + 
  ylab("Goldman et al. test accuracy, log scale") + 
  ggtitle("Goldman et al. test accuracy vs. training lemmas") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log(Goldman_train_lemmas), 
                  ymin = pred$fit - 2 * pred$se.fit, 
                  ymax = pred$fit + 2 * pred$se.fit), 
              fill = "grey", 
              alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_lemmas), y = predict(train_lemma_lm)), color = "black")
ggsave("../writeup/figs/lm_raw_lemmas.png", dpi=500)
Saving 6.94 x 4.29 in image

Training Size & Number of Training Lemmas

lemmas_plus_train_lm <- lm(log(Goldman_test_acc + 1) ~ log(Goldman_train_size) + log(Goldman_train_lemmas), data = df)
summary(lemmas_plus_train_lm)$r.squared
[1] 0.501522
compare_models(lemmas_plus_train_lm, train_lemma_lm)
[1] "AIC: 102.880219, ANOVA p: 0.060868"
  log(Goldman_train_size) log(Goldman_train_lemmas) 
                 4.900301                  4.900301 

compare_models(lemmas_plus_train_lm, train_size_lm)
[1] "AIC: 102.880219, ANOVA p: 0.533663"
  log(Goldman_train_size) log(Goldman_train_lemmas) 
                 4.900301                  4.900301 

Raw Accuracy Pirate Stats, Non-Linear Version 🏴‍☠️

It’s very clear from the above plots – more so than for test accuracy drop – that the relationships aren’t linear even on the log-log scale. As such, we once again make use of a natural cubic spline with df = 3 to extend these models to be non-linear and fit two single predictor models and one two-predictor model as above.

Training Size

ns_train_size_lm = lm(log(Goldman_test_acc + 1) ~ ns(log(Goldman_train_size), df = 3), data = df)
pred <- eval_model(ns_train_size_lm, df)
[1] "R^2: 0.551319, AIC: 101.301778"

df %>% 
  ggplot(aes(log(Goldman_train_size), log(Goldman_test_acc + 1))) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. training size, log scale") + 
  ylab("Goldman et al. test accuracy, log scale") + 
  ggtitle("Goldman et al. test accuracy vs. training size") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log(Goldman_train_size), 
                  ymin = pred$fit - 2 * pred$se.fit, 
                  ymax = pred$fit + 2 * pred$se.fit), 
              fill = "grey", 
              alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_size), y = pred$fit), color = "black")
ggsave("../writeup/figs/ns_raw_size.png", dpi=500)
Saving 6.94 x 4.29 in image

Training Lemmas

ns_train_lemma = lm(log(Goldman_test_acc + 1) ~ ns(log(Goldman_train_lemmas), df = 3), data = df)
pred <-  eval_model(ns_train_lemma, df)
[1] "R^2: 0.641152, AIC: 93.705729"

df %>% 
  ggplot(aes(log(Goldman_train_lemmas), log(Goldman_test_acc + 1))) + 
  geom_point(aes(colour = Family), size = 5, alpha = 0.5) + 
  scale_color_manual(values=c("turquoise", "purple", "gold")) + 
  theme_bw() + 
  xlab("Goldman et al. number of training lemmas, log scale") + 
  ylab("Goldman et al. test accuracy, log scale") + 
  ggtitle("Goldman et al. test accuracy vs. training lemmas") + 
  theme(plot.title = element_text(hjust=0.5, size=18), 
        axis.title.y = element_text(size=14),
        axis.title.x = element_text(size=14),
  )  + 
  geom_ribbon(aes(x = log(Goldman_train_lemmas), 
                  ymin = pred$fit - 2 * pred$se.fit, 
                  ymax = pred$fit + 2 * pred$se.fit), 
              fill = "grey", 
              alpha = .4) + 
  geom_line(aes(x = log(Goldman_train_lemmas), y = pred$fit), color = "black")

ggsave("../writeup/figs/ns_raw_lemmas.png", dpi=500)
Saving 6.94 x 4.29 in image

Training Size + Training Lemmas

ns_lemmas_plus_train_lm <- lm(log(Goldman_test_acc + 1) ~ ns(log(Goldman_train_size), df = 3) + ns(log(Goldman_train_lemmas), df = 3), data = df)
summary(ns_lemmas_plus_train_lm)$r.squared
[1] 0.6854902
compare_models(ns_lemmas_plus_train_lm, ns_train_lemma)
[1] "AIC: 95.221721, ANOVA p: 0.304902"
                                          GVIF Df GVIF^(1/(2*Df))
ns(log(Goldman_train_size), df = 3)   40.10359  3        1.850109
ns(log(Goldman_train_lemmas), df = 3) 40.10359  3        1.850109

compare_models(ns_lemmas_plus_train_lm, ns_train_size_lm)
[1] "AIC: 95.221721, ANOVA p: 0.020695"
                                          GVIF Df GVIF^(1/(2*Df))
ns(log(Goldman_train_size), df = 3)   40.10359  3        1.850109
ns(log(Goldman_train_lemmas), df = 3) 40.10359  3        1.850109

LS0tCnRpdGxlOiAiU3RhdGlzdGljcyBmb3IgUmVwbGljYXRpbmcgR29sZG1hbiBldCBhbC4gKDIwMjIpIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaGlzIG5vdGVib29rIGNvbnRhaW5zIGNvZGUgdG8gZXhlY3V0ZSBwbG90dGluZyBhbmQgZXhwbG9yYXRvcnkgc3RhdGlzdGljcyBmb3IgcmVwbGljYXRpbmcgR29sZG1hbiBldCBhbC4gKDIwMjIpIG9uIGEgc3Vic2V0IG9mIDMgbGFuZ3VhZ2UgZmFtaWxpZXM6IFVyYWxpYywgUm9tYW5jZSwgYW5kIE5pZ2VyLUNvbmdvLiBXZSBiZWdpbiBieSBpbnZlc3RpZ2F0aW5nIHByZWRpY3RvcnMgb2YgYWNjdXJhY3kgZHJvcCBiZXR3ZWVuIFNJR01PUlBIT04gKDIwMjApIGFuZCBHb2xkbWFuIGV0IGFsLiAoMjAyMiksIHRoZW4gaW52ZXN0aWdhdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJhdyBhY2N1cmFjeSBvbiB0aGUgR29sZG1hbiBldCBhbC4gdGVzdCBkYXRhIGFuZCB2YXJpb3VzIHByZWRpY3RvcnMuCgojIFNldHVwIAoKIyMjIyBMb2FkIExpYmFyaWVzIAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KG1nY3YpCnJlcXVpcmUoc3BsaW5lcykKYGBgCgojIyMjIFJlYWQgaW4gJiBQcm9jZXNzIHRoZSBEYXRhCgpgYGB7cn0KIyBSZWFkIGluIHRoZSBkYXRhIApkZjwtcmVhZC5jc3YoInJlcGxpY2F0aW9uL3JlcGxpY2F0aW9uX2NvbXBsZXRlLmNzdiIpCgojIE1ha2UgdGhlIGZhbWlseSBjb2x1bW4gcHJldHR5IApkZiRGYW1pbHkgPSBzdHJfdG9fdGl0bGUoc3RyX3JlcGxhY2UoZGYkRmFtaWx5LCAiXyIsICIgIikpCgojIFdlJ3JlIGdvaW5nIHRvIGJlIG1ha2luZyBhIGxvdCBvZiBsb2ctbG9nIHBsb3RzLCBzbyBsZXRzIG1ha2Ugb3VyIGxpdmVzIGVhc2llcjoKZGYkbG9nX3Rlc3RfYWNjX2Ryb3AgPSAtMSpsb2coKC0xKmRmJHRlc3RfYWNjX2Ryb3ApKzEpCmRmJGxvZ190cmFpbl9sZW1tYV9kaWZmID0gLTEqbG9nKC0xKmRmJHRyYWluX2xlbW1hX2RpZmZfcmF3KQpgYGAKCiMjIyMgRGVmaW5lIEhlbHBlciBGdW5jdGlvbnMgCgpIZXJlLCB3ZSBkZWZpbmUgaGVscGVyIGZ1bmN0aW9ucyB0byBydW4gb3VyIGNvcnJlbGF0aW9uYWwgc3RhdGlzdGljcywgZXZhbHVhdGUgb3VyIGZpdHRlZCBtb2RlbHMsIGFuZCBjb21wYXJlIG1vZGVscyBmaXR0ZWQgdG8gYSBzaW5nbGUgcHJlZGljdG9yIHRvIG1vZGVscyBmaXR0ZWQgdG8gbXVsdGlwbGUgcHJlZGljdG9ycyBpbiBvcmRlciB0byBldmFsdWF0ZSB3aGljaCBiZXR0ZXIgZXhwbGFpbnMgdGhlIGRhdGEuCgpgYGB7cn0KIyBXZSdsbCB1c2UgdGhpcyBoZWxwZXIgZnVuY3Rpb24gdG8gcnVuIG91ciBzdGF0cwpjb3JyZWxhdGlvbnMgPC0gZnVuY3Rpb24oYSwgYil7CiAgZm9yIChtIGluIGxpc3QoInBlYXJzb24iLCAic3BlYXJtYW4iLCAia2VuZGFsbCIpKXsKICAgICMgU3VwcmVzc2luZyB3YXJuaW5ncyBiYyB3ZSdsbCBnZXQgdGhlbSB3aGVuZXZlciB0aGVyZSBhcmUgdGllcyAKICAgIHN1cHByZXNzV2FybmluZ3MocmVzIDwtIGNvci50ZXN0KGEsIGIsIG1ldGhvZCA9IG0pKQogICAgZm9ybWF0dGVkIDwtIHNwcmludGYoIiVzOiAlZiAocCA9ICVlKSIsIHJlcyRtZXRob2QsIHJlcyRlc3RpbWF0ZSwgcmVzJHAudmFsdWUpCiAgICBwcmludChmb3JtYXR0ZWQpCiAgfQp9CgojIFdlJ2xsIHVzZSB0aGlzIGhlbHBlciBmdW5jdGlvbiB0byBnZXQgaW5mb3JtYXRpb24gYWJvdXQgb3VyIG1vZGVsCmV2YWxfbW9kZWwgPC0gZnVuY3Rpb24obW9kZWwsIGRmKXsKICByc3F1YXJlZCA9IHN1bW1hcnkobW9kZWwpJHIuc3F1YXJlZAogIEFJQyA9IEFJQyhtb2RlbCkKICByZXN1bHRzIDwtIHNwcmludGYoIlJeMjogJWYsIEFJQzogJWYiLCByc3F1YXJlZCwgQUlDKQogIHByaW50KHJlc3VsdHMpCiAgbGF5b3V0KG1hdHJpeChjKDEsMiwzLDQpLDIsMikpIAogIHBsb3QobW9kZWwpCiAgcmV0dXJuKHByZWRpY3QobW9kZWwsIGRmLCBzZSA9IFRSVUUpKQp9CgojIFdlJ2xsIHVzZSB0aGlzIGhlbHBlciBmdW5jdGlvbiB0byBjb21wYXJlIHR3byBtb2RlbHMgCmNvbXBhcmVfbW9kZWxzIDwtIGZ1bmN0aW9uKG1vZGVsX2JvdGgsIG1vZGVsX3NpbmdsZSl7CiAgbGF5b3V0KG1hdHJpeChjKDEsMiwzLDQpLDIsMikpIAogIHBsb3QobW9kZWxfYm90aCkKICBBSUMgPSBBSUMobW9kZWxfYm90aCkKICBhbm92YXAgPSBhbm92YShtb2RlbF9ib3RoLCBtb2RlbF9zaW5nbGUpJGBQcig+RilgWy0xXQogIHJlc3VsdHMgPC0gc3ByaW50ZigiQUlDOiAlZiwgQU5PVkEgcDogJWYiLCBBSUMsIGFub3ZhcCkKICBwcmludChyZXN1bHRzKQogIHZpZihtb2RlbF9ib3RoKQp9CmBgYAoKIyBJbnZlc3RpZ2F0aW5nIEFjY3VyYWN5IERyb3AKCldlIGJlZ2luIGJ5IGludmVzdGlnYXRpbmcgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRlc3QgYWNjdXJhY3kgZHJvcCAoYmV0d2VlbiBTSUdNT1JQSE9OIDIwMjAgJiBHb2xkbWFuIGV0IGFsIDIwMjIpIGFuZCB2YXJpb3VzIHByZWRpY3RvcnMuIEV4cGxvcmF0b3J5IGFuYWx5c2lzIHRlbGxzIHVzIHRoYXQgdGhlIGZvbGxvd2luZyBwcmVkaWN0b3JzIGhhdmUgc3Ryb25nIHJlbGF0aW9uc2hpcHMgd2l0aCBhY2N1cmFjeSBkcm9wOgoKLSAgICoqVHJhaW5pbmcgc2l6ZToqKiB0aGUgbnVtYmVyIG9mIHRyYWluaW5nIHRyaXBsZXMgaW4gdGhlIEdvbGRtYW4gZXQgYWwuIGRhdGEKCi0gICAqKlRyYWluaW5nIGxlbW1hczoqKiB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBsZW1tYXMgb2NjdXJyaW5nIGluIHRoZSBHb2xkbWFuIGV0IGFsLiBkYXRhCgotICAgKipMZW1tYSBkcm9wOioqIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBudW1iZXIgb2YgbGVtbWFzIGJldHdlZW4gdGhlIFNJR01PUlBIT04gZGF0YSBhbmQgdGhlIEdvbGRtYW4gZXQgYWwuIGRhdGEuCgpXZSBiZWdpbiBieSBpbnZlc3RpZ2F0aW5nIHVuc2NhbGVkIHNjYXR0ZXIgcGxvdHMgYmVmb3JlIGZpdHRpbmcgbGluZWFyIG1vZGVscyBhbmQgcnVubmluZyBzdGF0aXN0aWNzIG9uIHRoZSBsb2ctbG9nIHNjYWxlZCBwbG90cywgYW5kIGZpbmFsbHkgZml0dGluZyBub24tbGluZWFyIG1vZGVscyB0byB0aGVzZSBwcmVkaWN0b3JzLgoKIyMgVW5zY2FsZWQgU2NhdHRlciBQbG90cwoKIyMjIyBUcmFpbmluZyBTaXplCgpgYGB7cn0KZGYgJT4lIAogIGdncGxvdChhZXMoR29sZG1hbl90cmFpbl9zaXplLCB0ZXN0X2FjY19kcm9wKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBGYW1pbHkpLCBzaXplID0gNSwgYWxwaGEgPSAwLjUpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJ0dXJxdW9pc2UiLCAicHVycGxlIiwgImdvbGQiKSkgKyAKICB0aGVtZV9idygpICsgCiAgeGxhYigiR29sZG1hbiBldCBhbC4gdHJhaW5pbmcgc2l6ZSIpICsgCiAgeWxhYigiVGVzdCBhY2N1cmFjeSBkcm9wIikgKyAKICBnZ3RpdGxlKCJSZXBsaWNhdGlvbiBvZiBHb2xkbWFuIGV0IGFsLiIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTgpLCAKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgICkKYGBgCgojIyMjIFRyYWluaW5nIExlbW1hcwoKYGBge3J9CmRmICU+JSAKICBnZ3Bsb3QoYWVzKEdvbGRtYW5fdHJhaW5fbGVtbWFzLCB0ZXN0X2FjY19kcm9wKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBGYW1pbHkpLCBzaXplID0gNSwgYWxwaGEgPSAwLjUpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJ0dXJxdW9pc2UiLCAicHVycGxlIiwgImdvbGQiKSkgKyAKICB0aGVtZV9idygpICsgCiAgeGxhYigiR29sZG1hbiBldCBhbC4gdHJhaW5pbmcgbGVtbWFzIikgKyAKICB5bGFiKCJUZXN0IGFjY3VyYWN5IGRyb3AiKSArIAogIGdndGl0bGUoIlRlc3QgYWNjdXJhY3kgZHJvcCB2cy4gdHJhaW5pbmcgbGVtbWFzIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKQpgYGAKCiMjIyMgVHJhaW5pbmcgTGVtbWEgRHJvcAoKYGBge3J9CmRmICU+JSAKICBnZ3Bsb3QoYWVzKHRyYWluX2xlbW1hX2RpZmZfcmF3LCB0ZXN0X2FjY19kcm9wKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBGYW1pbHkpLCBzaXplID0gNSwgYWxwaGEgPSAwLjUpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJ0dXJxdW9pc2UiLCAicHVycGxlIiwgImdvbGQiKSkgKyAKICB0aGVtZV9idygpICsgCiAgeGxhYigiVHJhaW5pbmcgbGVtbWEgZGlmZmVyZW5jZSBiZXR3ZWVuIFNJR01PUlBIT04gJiBHb2xkbWFuIGV0IGFsLiIpICsgCiAgeWxhYigiVGVzdCBhY2N1cmFjeSBkcm9wIikgKyAKICBnZ3RpdGxlKCJUZXN0IGFjY3VyYWN5IGRyb3AgdnMuIHRyYWluaW5nIGxlbW1hIGRyb3AiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTE4KSwgCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICApCmBgYAoKIyMgTGluZWFyIE1vZGVscyBhbmQgQ29ycmVsYXRpb25zCgpGcm9tIHRoZSBhYm92ZSwgd2UgY2FuIHNlZSB0aGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaW91cyBwcmVkaWN0b3JzIGFuZCB0ZXN0IGFjY3VyYWN5IGRyb3AgaXNuJ3QgbGluZWFyLiBIb3dldmVyLCB3aGVuIGJvdGggYXhlcyBhcmUgbG9nLXNjYWxlZCwgdGhlIHJlbGF0aW9uc2hpcCBpcyBuZWFyIGxpbmVhci4gQXMgc3VjaCwgd2UgZml0IGxpbmVhciBtb2RlbHMgYW5kIHJ1biBjb3JyZWxhdGlvbiBzdGF0aXN0aWNzIG9uIHRoZSBsb2ctbG9nIHNjYWxlZCB2ZXJzaW9uLgoKIyMjIyBUcmFpbmluZyBzaXplIHZzLiB0ZXN0IGFjY3VyYWN5IGRyb3AKCmBgYHtyfQp0cmFpbl9zaXplX2xtID0gbG0obG9nX3Rlc3RfYWNjX2Ryb3AgfiBsb2coR29sZG1hbl90cmFpbl9zaXplKSwgZGF0YSA9IGRmKQpwcmVkIDwtIGV2YWxfbW9kZWwodHJhaW5fc2l6ZV9sbSwgZGYpCgpkZiAlPiUgCiAgZ2dwbG90KGFlcyhsb2coR29sZG1hbl90cmFpbl9zaXplKSwgbG9nX3Rlc3RfYWNjX2Ryb3ApKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJHb2xkbWFuIGV0IGFsLiB0cmFpbmluZyBzaXplLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIlRlc3QgYWNjdXJhY3kgZHJvcCwgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJUZXN0IGFjY3VyYWN5IGRyb3AgdnMuIHRyYWluaW5nIHNpemUiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTE4KSwgCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICApICArCiAgZ2VvbV9yaWJib24oYWVzKHggPSBsb2coR29sZG1hbl90cmFpbl9zaXplKSwgCiAgICAgICAgICAgICAgICAgIHltaW4gPSBwcmVkJGZpdCAtIDIgKiBwcmVkJHNlLmZpdCwgCiAgICAgICAgICAgICAgICAgIHltYXggPSBwcmVkJGZpdCArIDIgKiBwcmVkJHNlLmZpdCksIAogICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsIAogICAgICAgICAgICAgIGFscGhhID0gLjQpICsgCiAgZ2VvbV9saW5lKGFlcyh4ID0gbG9nKEdvbGRtYW5fdHJhaW5fc2l6ZSksIHkgPSBwcmVkJGZpdCksIGNvbG9yID0gImJsYWNrIikKZ2dzYXZlKCIuLi93cml0ZXVwL2ZpZ3MvbG1fZHJvcF9zaXplLnBuZyIsIGRwaT01MDApCgpjb3JyZWxhdGlvbnMobG9nKGRmJEdvbGRtYW5fdHJhaW5fc2l6ZSksIGRmJGxvZ190ZXN0X2FjY19kcm9wKQpgYGAKCiMjIyMgVHJhaW5pbmcgbGVtbWFzIHZzLiB0ZXN0IGFjY3VyYWN5IGRyb3AKCmBgYHtyfQp0cmFpbl9sZW1tYV9sbSA9IGxtKGxvZ190ZXN0X2FjY19kcm9wIH4gbG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgZGF0YSA9IGRmKQpwcmVkIDwtIGV2YWxfbW9kZWwodHJhaW5fbGVtbWFfbG0sIGRmKQoKZGYgJT4lIAogIGdncGxvdChhZXMobG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgbG9nX3Rlc3RfYWNjX2Ryb3ApKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJHb2xkbWFuIGV0IGFsLiBudW1iZXIgb2YgdHJhaW5pbmcgbGVtbWFzLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIlRlc3QgYWNjdXJhY3kgZHJvcCwgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJUZXN0IGFjY3VyYWN5IGRyb3AgdnMuIG51bWJlciBvZiB0cmFpbmluZyBsZW1tYXMiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTE4KSwgCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICApICArIAogIGdlb21fcmliYm9uKGFlcyh4ID0gbG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgCiAgICAgICAgICAgICAgICAgeW1pbiA9IHByZWQkZml0IC0gMiAqIHByZWQkc2UuZml0LCAKICAgICAgICAgICAgICAgICB5bWF4ID0gcHJlZCRmaXQgKyAyICogcHJlZCRzZS5maXQpLCAKICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsIAogICAgICAgICAgICAgYWxwaGEgPSAuNCkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSBsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpLCB5ID0gcHJlZCRmaXQpLCBjb2xvciA9ICJibGFjayIpCmdnc2F2ZSgiLi4vd3JpdGV1cC9maWdzL2xtX2Ryb3BfbGVtbWFzLnBuZyIsIGRwaT01MDApCgpjb3JyZWxhdGlvbnMobG9nKGRmJEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgZGYkbG9nX3Rlc3RfYWNjX2Ryb3ApCmBgYAoKIyMjIyBUcmFpbmluZyBsZW1tYSBkcm9wIHZzLiB0ZXN0IGFjY3VyYWN5IGRyb3AKCmBgYHtyfQpsZW1tYV9kcm9wX2xtID0gbG0obG9nX3Rlc3RfYWNjX2Ryb3AgfiBsb2dfdHJhaW5fbGVtbWFfZGlmZiwgZGF0YSA9IGRmKQpwcmVkIDwtIGV2YWxfbW9kZWwobGVtbWFfZHJvcF9sbSwgZGYpCgpkZiAlPiUgCiAgZ2dwbG90KGFlcyhsb2dfdHJhaW5fbGVtbWFfZGlmZiwgbG9nX3Rlc3RfYWNjX2Ryb3ApKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJUcmFpbmluZyBsZW1tYSBkcm9wLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIlRlc3QgYWNjdXJhY3kgZHJvcCwgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJUZXN0IGFjY3VyYWN5IGRyb3AgdnMuIHRyYWluaW5nIGxlbW1hIGRyb3AiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTgpLCAKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICkgICsgCiAgZ2VvbV9yaWJib24oYWVzKHggPSBsb2dfdHJhaW5fbGVtbWFfZGlmZiwgCiAgICAgICAgICAgICAgICAgeW1pbiA9IHByZWQkZml0IC0gMiAqIHByZWQkc2UuZml0LCAKICAgICAgICAgICAgICAgICB5bWF4ID0gcHJlZCRmaXQgKyAyICogcHJlZCRzZS5maXQpLCAKICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsIAogICAgICAgICAgICAgYWxwaGEgPSAuNCkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSBsb2dfdHJhaW5fbGVtbWFfZGlmZiwgeSA9IHByZWQkZml0KSwgY29sb3IgPSAiYmxhY2siKQoKZ2dzYXZlKCIuLi93cml0ZXVwL2ZpZ3MvbG1fZHJvcF9kcm9wLnBuZyIsIGRwaT01MDApCmNvcnJlbGF0aW9ucyhkZiRsb2dfdHJhaW5fbGVtbWFfZGlmZiwgZGYkbG9nX3Rlc3RfYWNjX2Ryb3ApCmBgYAoKIyMgQ28tTGluZWFyaXR5CgpJbnR1aXRpdmVseSwgaXQgbWFrZXMgc2Vuc2UgdGhhdCBzZXZlcmFsIG9mIHRoZSBwb3NzaWJsZSBwcmVkaWN0b3JzIGFib3ZlIHdvdWxkIGJlIGNvLWxpbmVhcjogbGFyZ2VyIHRyYWluaW5nIGRhdGEgd2lsbCBnZW5lcmFsbHkgY29udGFpbiBtb3JlIGxlbW1hcywgZm9yIGV4YW1wbGUuIFdlIGludmVzdGlnYXRlIHRoZXNlIGNvLWxpbmVhcml0aWVzIGJlbG93LgoKIyMjIyBUcmFpbmluZyBzaXplIHZzLiB0cmFpbmluZyBsZW1tYXMKCmBgYHtyfQpkZiAlPiUgCiAgZ2dwbG90KGFlcyhsb2coR29sZG1hbl90cmFpbl9zaXplKSwgbG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRmFtaWx5KSwgc2l6ZSA9IDUsIGFscGhhID0gMC41KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidHVycXVvaXNlIiwgInB1cnBsZSIsICJnb2xkIikpICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIkdvbGRtYW4gZXQgYWwuIHRyYWluaW5nIHNpemUsIGxvZyBzY2FsZSIpICsgCiAgeWxhYigiR29sZG1hbiBldCBhbC4gbnVtYmVyIG9mIHRyYWluaW5nIGxlbW1hcywgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJUcmFpbmluZyBzaXplIHZzLiB0cmFpbmluZyBsZW1tYXMsIGxvZyBzY2FsZSIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTgpLCAKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICkgICsgCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIsIHNpemU9MC41LCBhbHBoYSA9IDAuNSkKCmNvcnJlbGF0aW9ucyhsb2coZGYkR29sZG1hbl90cmFpbl9zaXplKSwgbG9nKGRmJEdvbGRtYW5fdHJhaW5fbGVtbWFzKSkKYGBgCgojIyMjIFRyYWluaW5nIHNpemUgdnMuIGRpZmZlcmVuY2UgaW4gdHJhaW5pbmcgbGVtbWFzCgpgYGB7cn0KZGYgJT4lCiAgZ2dwbG90KGFlcyhsb2coR29sZG1hbl90cmFpbl9zaXplKSwgbG9nX3RyYWluX2xlbW1hX2RpZmYpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJHb2xkbWFuIGV0IGFsLiB0cmFpbmluZyBzaXplLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIlRyYWluaW5nIGxlbW1hIGRpZmZlcmVuY2UsIGxvZyBzY2FsZSIpICsgCiAgZ2d0aXRsZSgiVHJhaW5pbmcgc2l6ZSB2cy4gdHJhaW5pbmcgbGVtbWEgZHJvcCIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTgpLCAKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICkgICsgCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIsIHNpemU9MC41LCBhbHBoYSA9IDAuNSkKCmNvcnJlbGF0aW9ucyhsb2coZGYkR29sZG1hbl90cmFpbl9zaXplKSwgZGYkbG9nX3RyYWluX2xlbW1hX2RpZmYpCmBgYAoKIyMjIyBUcmFpbmluZyBsZW1tYXMgdnMuIGRpZmZlcmVuY2UgaW4gdHJhaW5pbmcgbGVtbWFzCgpgYGB7cn0KZGYgJT4lCiAgZ2dwbG90KGFlcyhsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpLCBsb2dfdHJhaW5fbGVtbWFfZGlmZikpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRmFtaWx5KSwgc2l6ZSA9IDUsIGFscGhhID0gMC41KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidHVycXVvaXNlIiwgInB1cnBsZSIsICJnb2xkIikpICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIkdvbGRtYW4gZXQgYWwuIHRyYWluaW5nIGxlbW1hcywgbG9nIHNjYWxlIikgKyAKICB5bGFiKCJUcmFpbmluZyBsZW1tYSBkaWZmZXJlbmNlLCBsb2cgc2NhbGUiKSArIAogIGdndGl0bGUoIlRyYWluaW5nIGxlbW1hcyB2cy4gdHJhaW5pbmcgbGVtbWEgZHJvcCIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTgpLCAKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICkgICsgCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIsIHNpemU9MC41LCBhbHBoYSA9IDAuNSkKCmNvcnJlbGF0aW9ucyhsb2coZGYkR29sZG1hbl90cmFpbl9sZW1tYXMpLCBkZiRsb2dfdHJhaW5fbGVtbWFfZGlmZikKYGBgCgojIyBBY2N1cmFjeSBEcm9wIFBpcmF0ZSBTdGF0cywgTGluZWFyIFZlcnNpb24g8J+PtOKAjeKYoO+4jwoKV2Ugbm93IGhhdmUgdGhyZWUgcG9zc2libGUgcHJlZGljdG9yIHZhcmlhYmxlcyB3aGljaCB3ZSBrbm93IGFyZSBjby1saW5lYXIgd2l0aCBvbmUgYW5vdGhlciwgc28gd2Ugd2FudCB0byBkaXNlbnRhbmdsZSB0aGVzZSByZWxhdGlvbnNoaXBzIHRvIGRldGVybWluZSB3aGljaCB2YXJpYWJsZShzKSBiZXN0IHByZWRpY3QgZHJvcHMgaW4gbW9kZWwgcGVyZm9ybWFuY2UuCgpBYm92ZSwgd2Ugc2F3IHRoYXQgb2YgdGhlIHNpbmdsZS1wcmVkaWN0b3IgbGluZWFyIG1vZGVscywgdGVzdCBhY2N1cmFjeSBkcm9wIHdhcyBiZXN0IHByZWRpY3RlZCBieSB0cmFpbmluZyBsZW1tYXMuIFVzaW5nIHRoaXMgYXMgYSBzdGFydGluZyBwb2ludCwgd2UgYWRkIGFkZGl0aW9uYWwgcHJlZGljdG9ycyB0byB0aGUgbGluZWFyIG1vZGVscyB0byBkZXRlcm1pbmUgaWYgYW55IGFkZGl0aW9uYWwgcHJlZGljdG9yIGxlYWRzIHRvIHNpZ25pZmljYW50bHkgYmV0dGVyIG1vZGVsIHBlcmZvcm1hbmNlLCBkZWZpbmVkIGFzIGZvbGxvd3M6CgotICAgRG9lcyB0aGUgQUlDIGRyb3AgYnkgYXQgbGVhc3QgMiB1bml0cyAoc2lnbmlmaWNhbnRseSBiZXR0ZXIpP8KgCgotICAgSXMgdGhlIEFOT1ZBIHAtdmFsdWUgYmV0d2VlbiB0aGUgdHdvIG1vZGVscyBzaWduaWZpY2FudD8gKHAgXDwgMC4wNSkKCi0gICBJcyB0aGUgVklGIChhIG1lYXN1cmUgb2YgY28tbGluZWFyaXR5KSBsb3cg4oCTIGJlbG93IDMtND8KCiMjIyMgTGVtbWFzICsgVHJhaW4gU2l6ZQoKV2UgY29tcGFyZSBhIG1vZGVsIGNvbmRpdGlvbmVkIG9uIHRoZSBudW1iZXIgb2YgbGVtbWFzIGluIHRyYWluICphbmQqIHRoZSBudW1iZXIgb2YgdHJpcGxlcyBpbiB0cmFpbiB0byBvbmUganVzdCBjb25kaXRpb25lZCBvbiB0aGUgbnVtYmVyIG9mIGxlbW1hcy4KCmBgYHtyfQpsZW1tYXNfcGx1c190cmFpbl9sbSA9IGxtKGxvZ190ZXN0X2FjY19kcm9wIH4gbG9nKEdvbGRtYW5fdHJhaW5fc2l6ZSkgKyBsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpLCBkYXRhID0gZGYpCnN1bW1hcnkobGVtbWFzX3BsdXNfdHJhaW5fbG0pJHIuc3F1YXJlZApjb21wYXJlX21vZGVscyhsZW1tYXNfcGx1c190cmFpbl9sbSwgdHJhaW5fbGVtbWFfbG0pCmBgYAoKIyMjIyBMZW1tYXMgKyBMZW1tYSBEcm9wCgpXZSBjb21wYXJlIGEgbW9kZWwgY29uZGl0aW9uZWQgb24gdGhlIG51bWJlciBvZiBsZW1tYXMgaW4gdHJhaW4gKmFuZCogdGhlIGxlbW1hIGRyb3AgYmV0d2VlbiBTSUdNT1JQSE9OIGFuZCBHb2xkbWFuIGV0IGFsIGluIHRyYWluIHRvIG9uZSBqdXN0IGNvbmRpdGlvbmVkIG9uIHRoZSBudW1iZXIgb2YgbGVtbWFzLgoKYGBge3J9CmxlbW1hc19kcm9wX2xtID0gbG0obG9nX3Rlc3RfYWNjX2Ryb3AgfiBsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpICsgbG9nX3RyYWluX2xlbW1hX2RpZmYsIGRhdGEgPSBkZikKc3VtbWFyeShsZW1tYXNfZHJvcF9sbSkkci5zcXVhcmVkCmNvbXBhcmVfbW9kZWxzKGxlbW1hc19kcm9wX2xtLCB0cmFpbl9sZW1tYV9sbSkKYGBgCgojIyBBY2N1cmFjeSBEcm9wIFBpcmF0ZSBTdGF0cywgTm9uLUxpbmVhciBWZXJzaW9uIPCfj7TigI3imKDvuI8KClRob3VnaCB0aGUgcmVsYXRpb25zaGlwIGlzIG5lYXItbGluZWFyIHdoZW4gd2UgbG9nLXNjYWxlIGJvdGggYXhlcywgd2UgY2FuIHNlZSBmcm9tIG91ciByZXNpZHVhbCBwbG90cyBhYm92ZSB0aGF0IHRoZXJlIGlzIHNvbWUgbm9uLWxpbmVhcml0eSByZW1haW5pbmcuIEFzIHN1Y2gsIHdlIHRyYWluIG1vcmUgZ2VuZXJhbCBtb2RlbHMgdXNpbmcgdGhlIG5hdHVyYWwgY3ViaWMgc3BsaW5lcyB3aXRoIGBkZiA9IDNgLgoKIyMjIyBUcmFpbmluZyBzaXplCgpgYGB7cn0KbnNfdHJhaW5fc2l6ZV9sbSA9IGxtKGxvZ190ZXN0X2FjY19kcm9wIH4gbnMobG9nKEdvbGRtYW5fdHJhaW5fc2l6ZSksIGRmPTMpLCBkYXRhID0gZGYpCnByZWQgPC0gZXZhbF9tb2RlbChuc190cmFpbl9zaXplX2xtLCBkZikKCmRmICU+JSAKICBnZ3Bsb3QoYWVzKGxvZyhHb2xkbWFuX3RyYWluX3NpemUpLCBsb2dfdGVzdF9hY2NfZHJvcCkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRmFtaWx5KSwgc2l6ZSA9IDUsIGFscGhhID0gMC41KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidHVycXVvaXNlIiwgInB1cnBsZSIsICJnb2xkIikpICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIkdvbGRtYW4gZXQgYWwuIHRyYWluaW5nIHNpemUsIGxvZyBzY2FsZSIpICsgCiAgeWxhYigiVGVzdCBhY2N1cmFjeSBkcm9wLCBsb2cgc2NhbGUiKSArIAogIGdndGl0bGUoIlRlc3QgYWNjdXJhY3kgZHJvcCB2cy4gdHJhaW5pbmcgc2l6ZSIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTgpLCAKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICkgICsKICBnZW9tX3JpYmJvbihhZXMoeCA9IGxvZyhHb2xkbWFuX3RyYWluX3NpemUpLCAKICAgICAgICAgICAgICAgICAgeW1pbiA9IHByZWQkZml0IC0gMiAqIHByZWQkc2UuZml0LCAKICAgICAgICAgICAgICAgICAgeW1heCA9IHByZWQkZml0ICsgMiAqIHByZWQkc2UuZml0KSwgCiAgICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwgCiAgICAgICAgICAgICAgYWxwaGEgPSAuNCkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSBsb2coR29sZG1hbl90cmFpbl9zaXplKSwgeSA9IHByZWQkZml0KSwgY29sb3IgPSAiYmxhY2siKQoKZ2dzYXZlKCIuLi93cml0ZXVwL2ZpZ3MvbnNfZHJvcF9zaXplLnBuZyIsIGRwaT01MDApCmBgYAoKIyMjIyBUcmFpbiBsZW1tYXMKCmBgYHtyfQpuc190cmFpbl9sZW1tYV9sbSA9IGxtKGxvZ190ZXN0X2FjY19kcm9wIH4gbnMobG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgZGYgPSAzKSwgZGF0YSA9IGRmKQpwcmVkIDwtIGV2YWxfbW9kZWwobnNfdHJhaW5fbGVtbWFfbG0sIGRmKQoKZGYgJT4lIAogIGdncGxvdChhZXMobG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgbG9nX3Rlc3RfYWNjX2Ryb3ApKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJHb2xkbWFuIGV0IGFsLiBudW1iZXIgb2YgdHJhaW5pbmcgbGVtbWFzLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIlRlc3QgYWNjdXJhY3kgZHJvcCwgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJUZXN0IGFjY3VyYWN5IGRyb3AgdnMuIG51bWJlciBvZiB0cmFpbmluZyBsZW1tYXMiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplPTE4KSwgCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICApICArIAogIGdlb21fcmliYm9uKGFlcyh4ID0gbG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgCiAgICAgICAgICAgICAgICAgeW1pbiA9IHByZWQkZml0IC0gMiAqIHByZWQkc2UuZml0LCAKICAgICAgICAgICAgICAgICB5bWF4ID0gcHJlZCRmaXQgKyAyICogcHJlZCRzZS5maXQpLCAKICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsIAogICAgICAgICAgICAgYWxwaGEgPSAuNCkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSBsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpLCB5ID0gcHJlZCRmaXQpLCBjb2xvciA9ICJibGFjayIpCgpnZ3NhdmUoIi4uL3dyaXRldXAvZmlncy9uc19kcm9wX2xlbW1hcy5wbmciLCBkcGk9NTAwKQpgYGAKCiMjIyMgVHJhaW5pbmcgTGVtbWEgRHJvcAoKYGBge3J9Cm5zX2xlbW1hX2Ryb3BfbG0gPSBsbShsb2dfdGVzdF9hY2NfZHJvcCB+IG5zKGxvZ190cmFpbl9sZW1tYV9kaWZmLCBkZiA9IDMpLCBkYXRhID0gZGYpCnByZWQgPC0gZXZhbF9tb2RlbChuc19sZW1tYV9kcm9wX2xtLCBkZikKCmRmICU+JSAKICBnZ3Bsb3QoYWVzKGxvZ190cmFpbl9sZW1tYV9kaWZmLCBsb2dfdGVzdF9hY2NfZHJvcCkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRmFtaWx5KSwgc2l6ZSA9IDUsIGFscGhhID0gMC41KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidHVycXVvaXNlIiwgInB1cnBsZSIsICJnb2xkIikpICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIlRyYWluaW5nIGxlbW1hIGRyb3AsIGxvZyBzY2FsZSIpICsgCiAgeWxhYigiVGVzdCBhY2N1cmFjeSBkcm9wLCBsb2cgc2NhbGUiKSArIAogIGdndGl0bGUoIlRlc3QgYWNjdXJhY3kgZHJvcCB2cy4gdHJhaW5pbmcgbGVtbWEgZHJvcCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKSAgKyAKICBnZW9tX3JpYmJvbihhZXMoeCA9IGxvZ190cmFpbl9sZW1tYV9kaWZmLCAKICAgICAgICAgICAgICAgICB5bWluID0gcHJlZCRmaXQgLSAyICogcHJlZCRzZS5maXQsIAogICAgICAgICAgICAgICAgIHltYXggPSBwcmVkJGZpdCArIDIgKiBwcmVkJHNlLmZpdCksIAogICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwgCiAgICAgICAgICAgICBhbHBoYSA9IC40KSArIAogIGdlb21fbGluZShhZXMoeCA9IGxvZ190cmFpbl9sZW1tYV9kaWZmLCB5ID0gcHJlZCRmaXQpLCBjb2xvciA9ICJibGFjayIpCgpnZ3NhdmUoIi4uL3dyaXRldXAvZmlncy9uc19kcm9wX2Ryb3AucG5nIiwgZHBpPTUwMCkKYGBgCgpIYXZpbmcgdHJhaW5lZCB0aGUgcHJlbGltaW5hcnkgc2luZ2xlLXByZWRpY3RvciBtb2RlbHMsIHdlIGNhbiBvbmNlIGFnYWluIHRyYWluIHRoZSBtb3JlIGNvbXBsZXggbXVsdGktcHJlZGljdG9yIG1vZGVscyBhbmQgY29tcGFyZSB0aGVtIGluIHRlcm1zIG9mIEFJQywgQU5PVkEgcC12YWx1ZSwgYW5kIFZJRiBhcyBhYm92ZToKCiMjIyMgTGVtbWFzICsgVHJhaW4gU2l6ZQoKYGBge3J9Cm5zX2xlbW1hc19wbHVzX3RyYWluX2xtID0gbG0obG9nX3Rlc3RfYWNjX2Ryb3AgfiBucyhsb2coR29sZG1hbl90cmFpbl9zaXplKSwgZGYgPSAzKSArIG5zKGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIGRmID0gMyksIGRhdGEgPSBkZikKc3VtbWFyeShuc19sZW1tYXNfcGx1c190cmFpbl9sbSkkci5zcXVhcmVkCmNvbXBhcmVfbW9kZWxzKG5zX2xlbW1hc19wbHVzX3RyYWluX2xtLCBuc190cmFpbl9sZW1tYV9sbSkKYGBgCgojIyMjIExlbW1hcyArIExlbW1hIERyb3AKCmBgYHtyfQpuc19sZW1tYXNfZHJvcF9sbSA9IGxtKGxvZ190ZXN0X2FjY19kcm9wIH4gbnMobG9nKEdvbGRtYW5fdHJhaW5fbGVtbWFzKSwgZGYgPSAzKSArIG5zKGxvZ190cmFpbl9sZW1tYV9kaWZmLCBkZiA9IDMpLCBkYXRhID0gZGYpCnN1bW1hcnkobnNfbGVtbWFzX2Ryb3BfbG0pJHIuc3F1YXJlZApjb21wYXJlX21vZGVscyhuc19sZW1tYXNfZHJvcF9sbSwgbnNfdHJhaW5fbGVtbWFfbG0pCmBgYAoKIyBJbnZlc3RpZ2F0aW5nIFJhdyBBY2N1cmFjeQoKSGF2aW5nIGludmVzdGlnYXRlZCB0aGUgcmVsYXRpb25zaGlwcyB3aXRoIGFjY3VyYWN5IGRyb3AsIHdlIG5vdyB3aXNoIHRvIHVuZGVyc3RhbmQgdGhlIHByZWRpY3RvcnMgb2YgcmF3IGFjY3VyYWN5LiBUbyBiZWdpbiwgd2UgdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXAgd2l0aCByYXcgYWNjdXJhY3kgZm9yIGJvdGggU0lHTU9SUEhPTiBhbmQgR29sZG1hbiBldCBhbC4KCiMjIEJhc2ljIFZpc3VhbGl6YXRpb24KCkZpcnN0LCBmb3JtYXQgdGhlIGRhdGEgZm9yIGVhc2llciBwbG90dGluZzoKCmBgYHtyfQpHb2xkIDwtIGRmICU+JSAKICBzZWxlY3QodHJhaW49R29sZG1hbl90cmFpbl9zaXplLCAKICAgICAgICAgbGVtbWFzID0gR29sZG1hbl90cmFpbl9sZW1tYXMsCiAgICAgICAgIHRlc3QgPSBHb2xkbWFuX3Rlc3RfYWNjLCAKICAgICAgICAgRmFtaWx5ID0gRmFtaWx5CiAgICAgICAgICkgCkdvbGQkVHlwZSA9ICJHb2xkbWFuIgoKU2lnbSA8LSBkZiAlPiUgCiAgc2VsZWN0KHRyYWluID0gU0lHTU9SUEhPTl90cmFpbl9zaXplLAogICAgICAgICBsZW1tYXMgPSBTSUdNT1JQSE9OX3RyYWluX2xlbW1hcywKICAgICAgICAgdGVzdCA9IFNJR01PUlBIT05fdGVzdF9hY2MsCiAgICAgICAgIEZhbWlseSA9IEZhbWlseQogICAgICAgICApClNpZ20kVHlwZSA9ICJTSUdNT1JQSE9OIgoKbmV3X2RmID0gcmJpbmQoR29sZCwgU2lnbSkKYGBgCgojIyMjIFRyYWluaW5nIFNpemUKCmBgYHtyfQpuZXdfZGYgJT4lIAogIGdncGxvdChhZXMobG9nKHRyYWluKSwgbG9nKHRlc3QgKyAxKSwgY29sb3IgPSBUeXBlKSkgKwogIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gRmFtaWx5KSwgc2l6ZSA9IDUsIGFscGhhID0gMC41KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidHVycXVvaXNlIiwgInB1cnBsZSIsICJnb2xkIikpICsgCiAgc3RhdF9zbW9vdGgoYWVzKGNvbG9yPVR5cGUpLCBtZXRob2Q9ImxtIiwgc2l6ZT0wLjUsIGFscGhhID0gMC41KSsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIlRyYWluaW5nIHNpemUsIGxvZyBzY2FsZSIpICsgCiAgeWxhYigiVGVzdCBhY2N1cmFjeSwgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJUZXN0IGFjY3VyYWN5IHZzLiB0cmFpbmluZyBzaXplIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKSAKcHJpbnQoIi0tLS0tLS1HT0xETUFOIC0tLS0tLSIpCmNvcnJlbGF0aW9ucyhsb2coZGYkR29sZG1hbl90cmFpbl9zaXplKSwgbG9nKGRmJEdvbGRtYW5fdGVzdF9hY2MgKyAxKSkKcHJpbnQoIi0tLS0tLS1TSUdNT1JQSE9OIC0tLS0tLS0iKQpjb3JyZWxhdGlvbnMobG9nKGRmJFNJR01PUlBIT05fdHJhaW5fc2l6ZSksIGxvZyhkZiRTSUdNT1JQSE9OX3Rlc3RfYWNjICsgMSkpCmBgYAoKIyMjIyBUcmFpbmluZyBMZW1tYXMKCmBgYHtyfQpuZXdfZGYgJT4lIAogIGdncGxvdChhZXMobG9nKGxlbW1hcyksIGxvZyh0ZXN0ICsgMSksIGNvbG9yID0gVHlwZSkpICsKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHN0YXRfc21vb3RoKGFlcyhjb2xvcj1UeXBlKSwgbWV0aG9kPSJsbSIsIHNpemU9MC41LCBhbHBoYSA9IDAuNSkgKyAKICB0aGVtZV9idygpICsgCiAgeGxhYigiVHJhaW5pbmcgbGVtbWFzLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIlRlc3QgYWNjdXJhY3ksIGxvZyBzY2FsZSIpICsgCiAgZ2d0aXRsZSgiVGVzdCBhY2N1cmFjeSB2cy4gdHJhaW5pbmcgbGVtbWFzIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKSAKcHJpbnQoIi0tLS0tLS1HT0xETUFOIC0tLS0tLSIpCmNvcnJlbGF0aW9ucyhsb2coZGYkR29sZG1hbl90cmFpbl9sZW1tYXMpLCBsb2coZGYkR29sZG1hbl90ZXN0X2FjYyArIDEpKQpwcmludCgiLS0tLS0tLVNJR01PUlBIT04gLS0tLS0tLSIpCmNvcnJlbGF0aW9ucyhsb2coZGYkU0lHTU9SUEhPTl90cmFpbl9sZW1tYXMpLCBsb2coZGYkU0lHTU9SUEhPTl90ZXN0X2FjYyArIDEpKQpgYGAKCiMjIyMgQ28tTGluZWFyaXRpZXMgCgpgYGB7cn0KcHJpbnQoIi0tLS0tLS1HT0xETUFOIC0tLS0tLSIpCmNvcnJlbGF0aW9ucyhsb2coZGYkR29sZG1hbl90cmFpbl9zaXplKSwgbG9nKGRmJEdvbGRtYW5fdHJhaW5fbGVtbWFzKSkKcHJpbnQoIi0tLS0tLS1TSUdNT1JQSE9OIC0tLS0tLS0iKQpjb3JyZWxhdGlvbnMobG9nKGRmJFNJR01PUlBIT05fdHJhaW5fc2l6ZSksIGxvZyhkZiRTSUdNT1JQSE9OX3RyYWluX2xlbW1hcykpCmBgYAoKIyMgUmF3IEFjY3VyYWN5IFBpcmF0ZSBTdGF0cywgTGluZWFyIFZlcnNpb24g8J+PtOKAjeKYoO+4jwoKQXMgd2UgZGlkIGZvciBhY2N1cmFjeSBkcm9wLCB3ZSBub3cgZml0IGxpbmVhciBtb2RlbHMgdG8gcmF3IGFjY3VyYWN5IG9uIHRoZSBHb2xkbWFuIGV0IGFsLiBkYXRhIG9ubHkgKHNpbmNlIHRoZXJlIHNlZW1zIHRvIGJlIGxpdHRsZSBlZmZlY3QgZm9yIHRoZSBTSUdNT1JQSE9OIGRhdGEpLiBXZSB0aGVuIGNvbXBhcmUgdGhlIG1vZGVscyBmaXR0ZWQgdG8ganVzdCB0cmFpbmluZyBzaXplIG9yIHRyYWluaW5nIGxlbW1hcyB0byB0aGUgbW9kZWwgZml0dGVkIHRvIGJvdGggaW4gdGhlIHNhbWUgd2F5IGFzIGFib3ZlLgoKIyMjIyBUcmFpbmluZyBTaXplCgpgYGB7cn0KdHJhaW5fc2l6ZV9sbSA9IGxtKGxvZyhHb2xkbWFuX3Rlc3RfYWNjICsgMSkgfiBsb2coR29sZG1hbl90cmFpbl9zaXplKSwgZGF0YSA9IGRmKQpwcmVkIDwtIGV2YWxfbW9kZWwodHJhaW5fc2l6ZV9sbSwgZGYpCgpkZiAlPiUgCiAgZ2dwbG90KGFlcyhsb2coR29sZG1hbl90cmFpbl9zaXplKSwgbG9nKEdvbGRtYW5fdGVzdF9hY2MgKyAxKSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRmFtaWx5KSwgc2l6ZSA9IDUsIGFscGhhID0gMC41KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidHVycXVvaXNlIiwgInB1cnBsZSIsICJnb2xkIikpICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIkdvbGRtYW4gZXQgYWwuIHRyYWluaW5nIHNpemUsIGxvZyBzY2FsZSIpICsgCiAgeWxhYigiR29sZG1hbiBldCBhbC4gdGVzdCBhY2N1cmFjeSwgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJHb2xkbWFuIGV0IGFsLiB0ZXN0IGFjY3VyYWN5IHZzLiB0cmFpbmluZyBzaXplIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKSAgKyAKICBnZW9tX3JpYmJvbihhZXMoeCA9IGxvZyhHb2xkbWFuX3RyYWluX3NpemUpLCAKICAgICAgICAgICAgICAgICAgeW1pbiA9IHByZWQkZml0IC0gMiAqIHByZWQkc2UuZml0LCAKICAgICAgICAgICAgICAgICAgeW1heCA9IHByZWQkZml0ICsgMiAqIHByZWQkc2UuZml0KSwgCiAgICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwgCiAgICAgICAgICAgICAgYWxwaGEgPSAuNCkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSBsb2coR29sZG1hbl90cmFpbl9zaXplKSwgeSA9IHByZWQkZml0KSwgY29sb3IgPSAiYmxhY2siKSAKZ2dzYXZlKCIuLi93cml0ZXVwL2ZpZ3MvbG1fcmF3X3NpemUucG5nIiwgZHBpPTUwMCkKYGBgCgojIyMjIE51bWJlciBvZiBUcmFpbmluZyBMZW1tYXMKCmBgYHtyfQp0cmFpbl9sZW1tYV9sbSA9IGxtKGxvZyhHb2xkbWFuX3Rlc3RfYWNjICsgMSkgfiBsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpLCBkYXRhID0gZGYpCnByZWQgPC0gZXZhbF9tb2RlbCh0cmFpbl9sZW1tYV9sbSkKCmRmICU+JSAKICBnZ3Bsb3QoYWVzKGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIGxvZyhHb2xkbWFuX3Rlc3RfYWNjICsgMSkpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJHb2xkbWFuIGV0IGFsLiBudW1iZXIgb2YgdHJhaW5pbmcgbGVtbWFzLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIkdvbGRtYW4gZXQgYWwuIHRlc3QgYWNjdXJhY3ksIGxvZyBzY2FsZSIpICsgCiAgZ2d0aXRsZSgiR29sZG1hbiBldCBhbC4gdGVzdCBhY2N1cmFjeSB2cy4gdHJhaW5pbmcgbGVtbWFzIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKSAgKyAKICBnZW9tX3JpYmJvbihhZXMoeCA9IGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIAogICAgICAgICAgICAgICAgICB5bWluID0gcHJlZCRmaXQgLSAyICogcHJlZCRzZS5maXQsIAogICAgICAgICAgICAgICAgICB5bWF4ID0gcHJlZCRmaXQgKyAyICogcHJlZCRzZS5maXQpLCAKICAgICAgICAgICAgICBmaWxsID0gImdyZXkiLCAKICAgICAgICAgICAgICBhbHBoYSA9IC40KSArIAogIGdlb21fbGluZShhZXMoeCA9IGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIHkgPSBwcmVkaWN0KHRyYWluX2xlbW1hX2xtKSksIGNvbG9yID0gImJsYWNrIikKZ2dzYXZlKCIuLi93cml0ZXVwL2ZpZ3MvbG1fcmF3X2xlbW1hcy5wbmciLCBkcGk9NTAwKQpgYGAKCiMjIyMgVHJhaW5pbmcgU2l6ZSAmIE51bWJlciBvZiBUcmFpbmluZyBMZW1tYXMKCmBgYHtyfQpsZW1tYXNfcGx1c190cmFpbl9sbSA8LSBsbShsb2coR29sZG1hbl90ZXN0X2FjYyArIDEpIH4gbG9nKEdvbGRtYW5fdHJhaW5fc2l6ZSkgKyBsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpLCBkYXRhID0gZGYpCnN1bW1hcnkobGVtbWFzX3BsdXNfdHJhaW5fbG0pJHIuc3F1YXJlZApjb21wYXJlX21vZGVscyhsZW1tYXNfcGx1c190cmFpbl9sbSwgdHJhaW5fbGVtbWFfbG0pCmNvbXBhcmVfbW9kZWxzKGxlbW1hc19wbHVzX3RyYWluX2xtLCB0cmFpbl9zaXplX2xtKQpgYGAKCiMjIFJhdyBBY2N1cmFjeSBQaXJhdGUgU3RhdHMsIE5vbi1MaW5lYXIgVmVyc2lvbiDwn4+04oCN4pig77iPCgpJdCdzIHZlcnkgY2xlYXIgZnJvbSB0aGUgYWJvdmUgcGxvdHMg4oCTIG1vcmUgc28gdGhhbiBmb3IgdGVzdCBhY2N1cmFjeSBkcm9wIOKAkyB0aGF0IHRoZSByZWxhdGlvbnNoaXBzIGFyZW4ndCBsaW5lYXIgZXZlbiBvbiB0aGUgbG9nLWxvZyBzY2FsZS4gQXMgc3VjaCwgd2Ugb25jZSBhZ2FpbiBtYWtlIHVzZSBvZiBhIG5hdHVyYWwgY3ViaWMgc3BsaW5lIHdpdGggYGRmID0gM2AgdG8gZXh0ZW5kIHRoZXNlIG1vZGVscyB0byBiZSBub24tbGluZWFyIGFuZCBmaXQgdHdvIHNpbmdsZSBwcmVkaWN0b3IgbW9kZWxzIGFuZCBvbmUgdHdvLXByZWRpY3RvciBtb2RlbCBhcyBhYm92ZS4KCiMjIyMgVHJhaW5pbmcgU2l6ZQoKYGBge3J9Cm5zX3RyYWluX3NpemVfbG0gPSBsbShsb2coR29sZG1hbl90ZXN0X2FjYyArIDEpIH4gbnMobG9nKEdvbGRtYW5fdHJhaW5fc2l6ZSksIGRmID0gMyksIGRhdGEgPSBkZikKcHJlZCA8LSBldmFsX21vZGVsKG5zX3RyYWluX3NpemVfbG0sIGRmKQpkZiAlPiUgCiAgZ2dwbG90KGFlcyhsb2coR29sZG1hbl90cmFpbl9zaXplKSwgbG9nKEdvbGRtYW5fdGVzdF9hY2MgKyAxKSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRmFtaWx5KSwgc2l6ZSA9IDUsIGFscGhhID0gMC41KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidHVycXVvaXNlIiwgInB1cnBsZSIsICJnb2xkIikpICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIkdvbGRtYW4gZXQgYWwuIHRyYWluaW5nIHNpemUsIGxvZyBzY2FsZSIpICsgCiAgeWxhYigiR29sZG1hbiBldCBhbC4gdGVzdCBhY2N1cmFjeSwgbG9nIHNjYWxlIikgKyAKICBnZ3RpdGxlKCJHb2xkbWFuIGV0IGFsLiB0ZXN0IGFjY3VyYWN5IHZzLiB0cmFpbmluZyBzaXplIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKSAgKyAKICBnZW9tX3JpYmJvbihhZXMoeCA9IGxvZyhHb2xkbWFuX3RyYWluX3NpemUpLCAKICAgICAgICAgICAgICAgICAgeW1pbiA9IHByZWQkZml0IC0gMiAqIHByZWQkc2UuZml0LCAKICAgICAgICAgICAgICAgICAgeW1heCA9IHByZWQkZml0ICsgMiAqIHByZWQkc2UuZml0KSwgCiAgICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwgCiAgICAgICAgICAgICAgYWxwaGEgPSAuNCkgKyAKICBnZW9tX2xpbmUoYWVzKHggPSBsb2coR29sZG1hbl90cmFpbl9zaXplKSwgeSA9IHByZWQkZml0KSwgY29sb3IgPSAiYmxhY2siKQpnZ3NhdmUoIi4uL3dyaXRldXAvZmlncy9uc19yYXdfc2l6ZS5wbmciLCBkcGk9NTAwKQpgYGAKCiMjIyMgVHJhaW5pbmcgTGVtbWFzIAoKYGBge3J9Cm5zX3RyYWluX2xlbW1hID0gbG0obG9nKEdvbGRtYW5fdGVzdF9hY2MgKyAxKSB+IG5zKGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIGRmID0gMyksIGRhdGEgPSBkZikKcHJlZCA8LSAgZXZhbF9tb2RlbChuc190cmFpbl9sZW1tYSwgZGYpCmRmICU+JSAKICBnZ3Bsb3QoYWVzKGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIGxvZyhHb2xkbWFuX3Rlc3RfYWNjICsgMSkpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEZhbWlseSksIHNpemUgPSA1LCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInR1cnF1b2lzZSIsICJwdXJwbGUiLCAiZ29sZCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICB4bGFiKCJHb2xkbWFuIGV0IGFsLiBudW1iZXIgb2YgdHJhaW5pbmcgbGVtbWFzLCBsb2cgc2NhbGUiKSArIAogIHlsYWIoIkdvbGRtYW4gZXQgYWwuIHRlc3QgYWNjdXJhY3ksIGxvZyBzY2FsZSIpICsgCiAgZ2d0aXRsZSgiR29sZG1hbiBldCBhbC4gdGVzdCBhY2N1cmFjeSB2cy4gdHJhaW5pbmcgbGVtbWFzIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xOCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgKSAgKyAKICBnZW9tX3JpYmJvbihhZXMoeCA9IGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIAogICAgICAgICAgICAgICAgICB5bWluID0gcHJlZCRmaXQgLSAyICogcHJlZCRzZS5maXQsIAogICAgICAgICAgICAgICAgICB5bWF4ID0gcHJlZCRmaXQgKyAyICogcHJlZCRzZS5maXQpLCAKICAgICAgICAgICAgICBmaWxsID0gImdyZXkiLCAKICAgICAgICAgICAgICBhbHBoYSA9IC40KSArIAogIGdlb21fbGluZShhZXMoeCA9IGxvZyhHb2xkbWFuX3RyYWluX2xlbW1hcyksIHkgPSBwcmVkJGZpdCksIGNvbG9yID0gImJsYWNrIikKCmdnc2F2ZSgiLi4vd3JpdGV1cC9maWdzL25zX3Jhd19sZW1tYXMucG5nIiwgZHBpPTUwMCkKYGBgCgojIyMjIFRyYWluaW5nIFNpemUgKyBUcmFpbmluZyBMZW1tYXMKCmBgYHtyfQpuc19sZW1tYXNfcGx1c190cmFpbl9sbSA8LSBsbShsb2coR29sZG1hbl90ZXN0X2FjYyArIDEpIH4gbnMobG9nKEdvbGRtYW5fdHJhaW5fc2l6ZSksIGRmID0gMykgKyBucyhsb2coR29sZG1hbl90cmFpbl9sZW1tYXMpLCBkZiA9IDMpLCBkYXRhID0gZGYpCnN1bW1hcnkobnNfbGVtbWFzX3BsdXNfdHJhaW5fbG0pJHIuc3F1YXJlZApjb21wYXJlX21vZGVscyhuc19sZW1tYXNfcGx1c190cmFpbl9sbSwgbnNfdHJhaW5fbGVtbWEpCmNvbXBhcmVfbW9kZWxzKG5zX2xlbW1hc19wbHVzX3RyYWluX2xtLCBuc190cmFpbl9zaXplX2xtKQpgYGAK